/**
 * Copyright 2009-2019 the original author or authors.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.ibatis.session;

import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;

/**
 * Builds {@link SqlSessionFactory} instances.
 * <p>
 * 构建SqlSessionFactory实例
 * <p>
 * SqlSessionFactoryBuilder提供了三种类型的构建方法:
 * <li>1. Reader
 * <li>2. InputStream
 * <li>3. Configuration
 *
 * @author Clinton Begin
 */
public class SqlSessionFactoryBuilder {

    /**
     * 重载实例化方法
     * <p>
     * 根据Reader实例构造SqlSessionFactory
     *
     * @param reader reader
     * @return SqlSessionFactory
     */
    public SqlSessionFactory build(Reader reader) {
        return build(reader, null, null);
    }

    /**
     * 重载实例化方法
     * <p>
     * 根据Reader和环境标识实例化SqlSessionFactory
     *
     * @param reader      reader
     * @param environment 环境标志
     * @return
     */
    public SqlSessionFactory build(Reader reader, String environment) {
        return build(reader, environment, null);
    }

    /**
     * 重载实例化方法
     * <p>
     * 根据Reader和Properties配置实例化SqlSessionFactory
     *
     * @param reader     reader
     * @param properties 属性配置
     * @return
     */
    public SqlSessionFactory build(Reader reader, Properties properties) {
        return build(reader, null, properties);
    }

    /**
     * 重载实例化方法
     * <p>
     * 真正的实例化方法。其他重载方法全部调用此方法
     * <p>
     * 此方法会先实例化XMLConfigBuilder, 然后根据入参文件流, 实例化成Configuration
     *
     * @param reader      reader
     * @param environment 环境标志
     * @param properties  变量文件
     * @return
     */
    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        try {
            // 实例化configBuilder
            XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
            // 解析config为Configuration对象
            Configuration config = parser.parse();
            return build(config);
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                reader.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }

    /**
     * 构造SqlSessionFactory
     *
     * @param inputStream
     * @return
     */
    public SqlSessionFactory build(InputStream inputStream) {
        return build(inputStream, null, null);
    }

    public SqlSessionFactory build(InputStream inputStream, String environment) {
        return build(inputStream, environment, null);
    }

    public SqlSessionFactory build(InputStream inputStream, Properties properties) {
        return build(inputStream, null, properties);
    }

    /**
     * 根据参数
     *
     * @param inputStream
     * @param environment
     * @param properties
     * @return
     */
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            return build(parser.parse());
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                inputStream.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }

    /**
     * 创建默认会话工厂
     *
     * @param config mybatis配置对象
     * @return
     */
    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }

}
